﻿// file:	katomic.c
#include "pch.h"
#define SB_ATOM_IMPL	1
#include "sbcs.h"
#if _SB_APPLE_
#include <libkern/OSAtomic.h>
#endif

//////////////////////////////////////////////////////////////////////////
// 아토믹

#if _SB_WINDOWS_
// 윈도우용
#if _SB_WINDOWS_DESKTOP_ && _MSC_VER
// 윈도우 데스크탑과 MSC일때
#define InterlockedIncrement			_InterlockedIncrement
#define InterlockedDecrement			_InterlockedDecrement
#ifndef InterlockedExchangeAdd
#define InterlockedExchangeAdd			_InterlockedExchangeAdd
#endif
#define InterlockedExchange				_InterlockedExchange
#define InterlockedCompareExchange		_InterlockedCompareExchange
#define InterlockedIncrement64			_InterlockedIncrement64
#define InterlockedDecrement64			_InterlockedDecrement64
#ifndef InterlockedExchangeAdd64
#define InterlockedExchangeAdd64		_InterlockedExchangeAdd64
#endif
#define InterlockedExchange64			_InterlockedExchange64
#define InterlockedCompareExchange64	_InterlockedCompareExchange64
#endif

/**
 * 아토믹 증가 연산.
 * @param [입력,반환]	value	아토믹 변수.
 * @return	증가 값.
 */
int k_atominc(int volatile* value)
{
	return (int)InterlockedIncrement((LONG volatile*)value);
}

/**
 * 아토믹 감소 연산.
 * @param [입력,반환]	value	아토믹 변수.
 * @return	감소 값.
 */
int k_atomdec(int volatile* value)
{
	return (int)InterlockedDecrement((LONG volatile*)value);
}

/**
 * 아토믹 더하기 연산.
 * @param [입력,반환]	value	아토믹 변수.
 * @param	amt			 	증가 량.
 * @return	결과 값.
 */
int k_atomadd(int volatile* value, kint amt)
{
	return (int)InterlockedExchangeAdd((LONG volatile*)value, (LONG)amt);
}

/**
 * 아토믹 교환 연산.
 * @param [입력,반환]	p	아토믹 변수.
 * @param	value	 	교환할 값.
 * @return	결과 값.
 */
int k_atomexch(int volatile* p, int value)
{
	return (int)InterlockedExchange((LONG volatile*)p, (LONG)value);
}

/**
 * 아토믹 비교 연산.
 * @param [입력,반환]	p	아토믹 변수.
 * @param	value	 	변경할 값.
 * @param	comperand	비교할 값.
 * @return	결과 값.
 */
int k_atomcmp(int volatile* p, int value, int comperand)
{
	return (int)InterlockedCompareExchange((LONG volatile*)p, (LONG)value, (LONG)comperand);
}

#if _SB_64_
/// <summary> 아토믹 증가 연산. </summary>
/// <param name="value"> [입력,반환] 아토믹 변수. </param>
/// <returns> 증가 값 </returns>
klong k_atominc_long(klong volatile* value)
{
	return (klong)InterlockedIncrement64((klong volatile*)value);
}

/// <summary> 아토믹 감소 연산. </summary>
/// <param name="value"> [입력,반환] 아토믹 변수. </param>
/// <returns> 감소 값. </returns>
klong k_atomdec_long(klong volatile* value)
{
	return (klong)InterlockedDecrement64((klong volatile*)value);
}

/// <summary> 아토믹 더하기 연산. </summary>
/// <param name="value"> [입력,반환] 아토믹 변수. </param>
/// <param name="amt">  증가 량. </param>
/// <returns> 결과 값. </returns>
klong k_atomadd_long(klong volatile* value, klong amt)
{
	return (klong)InterlockedExchangeAdd64((klong volatile*)value, (klong)amt);
}

/// <summary> 아토믹 교환 연산. </summary>
/// <param name="p">	 [입력,반환] 아토믹 변수. </param>
/// <param name="value"> 교환할 값. </param>
/// <returns> 결과 값. </returns>
klong k_atomexch_long(klong volatile* p, klong value)
{
	return (klong)InterlockedExchange64((klong volatile*)p, (klong)value);
}

/// <summary> 아토믹 비교 연산. </summary>
/// <param name="p">   [입력,반환] 아토믹 변수. </param>
/// <param name="value">	 변경할 값. </param>
/// <param name="comperand"> 비교할 값. </param>
/// <returns> 결과 값. </returns>
klong k_atomcmp_long(klong volatile* p, klong value, klong comperand)
{
	return (klong)InterlockedCompareExchange64((klong volatile*)p, (klong)value, (klong)comperand);
}
#endif
#elif __GNUC__ && !_SB_KIBOT_
// GCC용, 키봇1(ARM)은 지원안함
int k_atominc(int volatile* value)
{
	return (int)__sync_fetch_and_add(value, 1) + 1;
}

int k_atomdec(int volatile* value)
{
	return (int)__sync_fetch_and_sub(value, 1) - 1;
}

int k_atomadd(int volatile* value, kint amt)
{
	return (int)__sync_fetch_and_add(value, amt);
}

int k_atomexch(int volatile* value, int exch)
{
	return (int)__sync_lock_test_and_set(value, exch);
}

int k_atomcmp(int volatile* value, int exch, int comperand)
{
	return (int)__sync_val_compare_and_swap(value, comperand, exch);
}

#if _SB_64_
klong k_atominc_long(klong volatile* value)
{
	return (klong)__sync_fetch_and_add(value, 1) + 1;
}

klong k_atomdec_long(klong volatile* value)
{
	return (klong)__sync_fetch_and_sub(value, 1) - 1;
}

klong k_atomadd_long(klong volatile* value, klong amt)
{
	return (klong)__sync_fetch_and_add(value, amt);
}

klong k_atomexch_long(klong volatile* p, klong exch)
{
	return (klong)__sync_lock_test_and_set(p, exch);	
}

klong k_atomcmp_long(klong volatile* p, klong exch, klong comperand)
{
#if !_SB_ANDROID_ || (_SB_ANDROID_ && _SB_64_)
	return (klong)__sync_val_compare_and_swap(p, comperand, exch);
#else
	klong ret;
	ku_lock();
	ret=*p;
	if (ret==comperand)
		*p=exch;
	ku_unlock();
	return ret;
#endif
}
#endif
#elif _SB_APPLE_
// 애플 OSX, IOS
int k_atominc(int volatile* value)
{
	return OSAtomicIncrement32(value);
}

int k_atomdec(int volatile* value)
{
	return OSAtomicDecrement32(value);
}

int k_atomadd(int volatile* value, kint amt)
{
	return OSAtomicAdd32(amt, value);
}

int k_atomexch(int volatile* value, int exch)
{
	int n;
	do n = *value;
	while (!OSAtomicCompareAndSwap32Barrier(n, exch, value));
	return n;
}

int k_atomcmp(int volatile* value, int exch, int comperand)
{
	return OSAtomicCompareAndSwap32(value, exch, comperand);
}

#if _SB_64_
klong k_atominc_long(klong volatile* value)
{
	return OSAtomicIncrement64(value);
}

klong k_atomdec_long(klong volatile* value)
{
	return OSAtomicDecrement64(value);
}

klong k_atomadd_long(klong volatile* value, klong amt)
{
	return OSAtomicAdd64(amt, value, amt);
}

klong k_atomexch_long(klong volatile* value, klong exch)
{
	klong n;
	do n = *value;
	while (!OSAtomicCompareAndSwap32Barrier(n, exch, value));
	return n;
}

klong k_atomcmp_long(klong volatile* p, klong value, klong comperand)
{
	return OSAtomicCompareAndSwap64(value, exch, comperand);
}
#endif

void k_read_write_barrier(void)
{
	kCfs _cfs_ = KLOCK_INIT; 
	k_cfs_enter(&_cfs_); 
	k_cfs_leave(&_cfs_);
}

void k_mm_pause(void)
{
	// 해당 기능 없음
}
#elif _SB_X86_
// 알 수 없는 X86 어셈블리

// 여기까지 오면 무슨 컴파일러를 쓰는것이쇼?
#error Compiler do not support atomic operations

// 아래 코드는 GCC ASM 백업용
K_INLINE int __atomic_inc(volatile int* value)
{
	int result = 0;
	__asm__ __volatile__(
		"lock; xaddl %0, (%1)"
		: "=r"(result)
		: "r"(value), "0"(1)
		: "memory"
		);
	return (result + 1);
}

K_INLINE int __atomic_dec(volatile int* value)
{
	int result = 0;
	__asm__ __volatile__(
		"lock; xaddl %0, (%1)"
		: "=r"(result)
		: "r"(value), "0"(-1)
		: "memory"
		);
	return (result - 1);
}

K_INLINE int __atomic_swap(int value, volatile int* p)
{
	int result = 0;
	__asm__ __volatile__("lock; xchgl %0,(%1)"
		: "=r"(result) : "r"(p), "0"(value) : "memory");
	return result;
}

K_INLINE int __atomic_cmpxchg(int value, int comperand, volatile int* p)
{
	int result;
	__asm__ __volatile__("lock; cmpxchgl %2,(%1)"
		: "=a"(result) : "r"(p), "r"(value), "0"(comperand) : "memory");
	return result;
}

#if _SB_64_
klong k_atominc_long(klong volatile* ptr)
{
	klong result = 0;
	__asm__ __volatile__(
		"lock; xaddq %0, (%1)"
		: "=r"(result)
		: "r"(ptr), "0"(1)
		: "memory"
		);
	return (result + 1);
}

klong k_atomdec_long(klong volatile* ptr)
{
	klong result = 0;
	__asm__ __volatile__(
		"lock; xaddq %0, (%1)"
		: "=r"(result)
		: "r"(ptr), "0"(-1)
		: "memory"
		);
	return (result - 1);
}

klong k_atomexch_long(klong volatile* ptr, klong _new)
{
	klong ret;
	__asm__ __volatile__("lock; xchgq %0,(%1)"
		: "=r"(ret) : "r"(ptr), "0"(_new) : "memory");
	return ret;
}

klong k_atomcmp_long(klong volatile* ptr, klong value, klong comperand)
{
	klong ret;
	__asm__ __volatile__("lock; cmpxchgq %2,(%1)"
		: "=a"(ret) : "r"(ptr), "r"(value), "0"(comperand) : "memory");
	return ret;
}
#endif
#else
// 알 수 없는 프로세서 & 컴파일러
// 주로 암계열, 일단 어셈블러 문제로 그냥 아토믹 안함 (pthread)
// : http://211.43.222.7/projects/linux/src/arch/arm/include/asm/atomic.h 
// : kibot -> 안됨
// : kibot2, android <sys/atomics.h> -> 안됨
// : kibot2 -> 됨!!!! 위의 sync로 처리함 [2014-04-14 ksh]

int k_atominc(volatile int* value)
{
	int ret;

	ku_lock();

	*value = *value + 1;
	ret = *value;

	ku_unlock();

	return ret;
}

int k_atomdec(volatile int* value)
{
	int ret;

	ku_lock();

	*value = *value - 1;
	ret = *value;

	ku_unlock();

	return ret;
}

int k_atomexch(volatile int* value, int exch)
{
	int ret;

	ku_lock();

	*value = exch;
	ret = *value;

	ku_unlock();

	return ret;
}

int k_atomcmp(volatile int* value, int exch, int comperand)
{
	int ret;

	ku_lock();

	ret = *value;

	if (ret == comperand)
		*value = exch;

	ku_unlock();

	return ret;
}

#if _SB_64_
klong k_atominc_long(volatile klong* value)
{
	klong ret;

	ku_lock();

	*value = *value + 1;
	ret = *value;

	ku_unlock();

	return ret;
}

klong k_atomdec_long(volatile klong* value)
{
	klong ret;

	ku_lock();

	*value = *value - 1;
	ret = *value;

	ku_unlock();

	return ret;
}

klong k_atomexch_long(volatile klong* value, klong exch)
{
	klong ret;

	ku_lock();

	*value = exch;
	ret = *value;

	ku_unlock();

	return ret;
}

klong k_atomcmp_long(volatile klong* value, klong exch, klong comperand)
{
	klong ret;

	ku_lock();

	ret = *value;

	if (ret == comperand)
		*value = exch;

	ku_unlock();

	return ret;
}
#endif

void k_read_write_barrier(void)
{
#if !_SB_WINDOWS_
	kCfs _cfs_ = KLOCK_INIT; 
	k_cfs_enter(&_cfs_); 
	k_cfs_leave(&_cfs_);
#endif
}

void k_mm_pause(void)
{
#if !_SB_WINDOWS_
	k_usleep(0);
#endif
}
#endif
